Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


Examples/Speedtest.py 72db6e0ef2e9da96b5f164eb7c37ddbf3022bdec (72db6e0e) Text, 10.49 KB

T8b949e##########################################################
T8b949e# This RNS example demonstrates a simple speedtest #
T8b949e# program to measure link throughput. #
T8b949e# #
T8b949e# The current configuration is suited for testing fast #
T8b949e# links. If you want to measure slow links like LoRa or #
T8b949e# packet radio, you must significantly lower the #
T8b949e# data_cap variable, which defines how much data is sent #
T8b949e# for each test. #
T8b949e##########################################################

Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72import T7ee787RNS

T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create.
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"

T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################

Te6edf3latest_client_link Tff7b72= Tff7b72None
Te6edf3first_packet_at Tff7b72= Tff7b72None
Te6edf3last_packet_at Tff7b72= Tff7b72None
Te6edf3received_data Tff7b72= T79c0ff0
Te6edf3rc Tff7b72= T79c0ff0
Te6edf3data_cap Tff7b72= T79c0ff2Tff7b72*T79c0ff1024Tff7b72*T79c0ff1024
Te6edf3printed Tff7b72= Tff7b72False

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)

T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffspeedtestTa5d6ff"
Tb4b4b4)

T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)

T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)

Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffSpeedtest Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)

T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)

T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_linkTb4b4b4, Te6edf3first_packet_atTb4b4b4, Te6edf3rc

Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3first_packet_at Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3rc Tff7b72= T79c0ff0
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3server_packet_receivedTb4b4b4)
Te6edf3latest_client_link Tff7b72= Te6edf3link

Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)


T8b949e# A convenience function for printing a human-
T8b949e# readable file size
Tff7b72def Td2a8ffsize_strTb4b4b4(Te6edf3numTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffBTa5d6ff'Tb4b4b4)Tb4b4b4:
Te6edf3units Tff7b72= Tb4b4b4[Ta5d6ff'Ta5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffKiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffMiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffGiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffTiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffPiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffEiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffZiTa5d6ff'Tb4b4b4]
Te6edf3last_unit Tff7b72= Ta5d6ff'Ta5d6ffYiTa5d6ff'

Tff7b72if Te6edf3suffix Tff7b72== Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4:
Te6edf3num Tff7b72*Tff7b72= T79c0ff8
Te6edf3units Tff7b72= Tb4b4b4[Ta5d6ff'Ta5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffKTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffMTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffGTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffTTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffPTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffETa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffZTa5d6ff'Tb4b4b4]
Te6edf3last_unit Tff7b72= Ta5d6ff'Ta5d6ffYTa5d6ff'

Tff7b72for Te6edf3unit Tff7b72in Te6edf3unitsTb4b4b4:
Tff7b72if Tffa657absTb4b4b4(Te6edf3numTb4b4b4) Tff7b72< T79c0ff1024.0Tb4b4b4:
Tff7b72return Ta5d6ff"Tffd700%3.2fTa5d6ff Tffd700%sTffd700%sTa5d6ff" Tff7b72% Tb4b4b4(Te6edf3numTb4b4b4, Te6edf3unitTb4b4b4, Te6edf3suffixTb4b4b4)
Te6edf3num Tff7b72/Tff7b72= T79c0ff1024.0
Tff7b72return Ta5d6ff"Tffd700%.2fTa5d6ff Tffd700%sTffd700%sTa5d6ff" Tff7b72% Tb4b4b4(Te6edf3numTb4b4b4, Te6edf3last_unitTb4b4b4, Te6edf3suffixTb4b4b4)


Tff7b72def Td2a8ffserver_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_linkTb4b4b4, Te6edf3first_packet_atTb4b4b4, Te6edf3last_packet_atTb4b4b4, Te6edf3received_dataTb4b4b4, Te6edf3rcTb4b4b4, Te6edf3data_cap
Te6edf3received_data Tff7b72+Tff7b72= Tffa657lenTb4b4b4(Te6edf3packetTff7b72.Td2a8ffdataTb4b4b4)
Te6edf3rc Tff7b72+Tff7b72= T79c0ff1
Tff7b72if Te6edf3rc Tff7b72>Tff7b72= T79c0ff50Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Te6edf3size_strTb4b4b4(Te6edf3received_dataTb4b4b4)Tb4b4b4)
Te6edf3rc Tff7b72= T79c0ff0

Tff7b72if Te6edf3received_data Tff7b72> Te6edf3data_capTb4b4b4:
Te6edf3rcv_d Tff7b72= Te6edf3received_data
Te6edf3received_data Tff7b72= T79c0ff0
Te6edf3rc Tff7b72= T79c0ff0

Te6edf3last_packet_at Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
T8b949e# Print statistics
Te6edf3download_time Tff7b72= Te6edf3last_packet_atTff7b72-Te6edf3first_packet_at
Te6edf3hoursTb4b4b4, Te6edf3rem Tff7b72= Tffa657divmodTb4b4b4(Te6edf3download_timeTb4b4b4, T79c0ff3600Tb4b4b4)
Te6edf3minutesTb4b4b4, Te6edf3seconds Tff7b72= Tffa657divmodTb4b4b4(Te6edf3remTb4b4b4, T79c0ff60Tb4b4b4)
Te6edf3timestring Tff7b72= Ta5d6ff"Tffd700{:0>2}Ta5d6ff:Tffd700{:0>2}Ta5d6ff:Tffd700{:05.2f}Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657intTb4b4b4(Te6edf3hoursTb4b4b4)Tb4b4b4,Tffa657intTb4b4b4(Te6edf3minutesTb4b4b4)Tb4b4b4,Te6edf3secondsTb4b4b4)

Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff--- Statistics -----Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTime taken : Ta5d6ff"Tff7b72+Te6edf3timestringTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffData transferred : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3rcv_dTb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTransfer rate : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3rcv_dTff7b72/Te6edf3download_timeTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff/sTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)

Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Te6edf3latest_client_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.2Tb4b4b4)
Te6edf3rc Tff7b72= T79c0ff0
Te6edf3received_data Tff7b72= T79c0ff0


T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################

T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
Te6edf3should_quit Tff7b72= Tff7b72False

T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)

T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)

T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)

T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)

T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffspeedtestTa5d6ff"
Tb4b4b4)

T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)

T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)

T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)

Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_linkTb4b4b4, Te6edf3should_quit

T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)

Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.2Tb4b4b4)

T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_linkTb4b4b4, Te6edf3data_capTb4b4b4, Te6edf3printed
Te6edf3server_link Tff7b72= Te6edf3link
Te6edf3data_sent Tff7b72= T79c0ff0

T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, sending...Ta5d6ff"Tb4b4b4)
Te6edf3rd Tff7b72= Te6edf3osTff7b72.Td2a8ffurandomTb4b4b4(Te6edf3linkTff7b72.Td2a8ffmduTb4b4b4)
Te6edf3started Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tff7b72while Te6edf3linkTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffACTIVE Tff7b72and Te6edf3data_sent Tff7b72< Te6edf3data_capTff7b72*T79c0ff1.25Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3server_linkTb4b4b4, Te6edf3rdTb4b4b4, Te6edf3create_receiptTff7b72=Tff7b72FalseTb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
Te6edf3data_sent Tff7b72+Tff7b72= Tffa657lenTb4b4b4(Te6edf3rdTb4b4b4)

Tff7b72if Te6edf3data_sent Tff7b72> Te6edf3data_cap Tff7b72and Tff7b72not Te6edf3printedTb4b4b4:
Te6edf3printed Tff7b72= Tff7b72True
Te6edf3ended Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
T8b949e# Print statistics
Te6edf3download_time Tff7b72= Te6edf3endedTff7b72-Te6edf3started
Te6edf3hoursTb4b4b4, Te6edf3rem Tff7b72= Tffa657divmodTb4b4b4(Te6edf3download_timeTb4b4b4, T79c0ff3600Tb4b4b4)
Te6edf3minutesTb4b4b4, Te6edf3seconds Tff7b72= Tffa657divmodTb4b4b4(Te6edf3remTb4b4b4, T79c0ff60Tb4b4b4)
Te6edf3timestring Tff7b72= Ta5d6ff"Tffd700{:0>2}Ta5d6ff:Tffd700{:0>2}Ta5d6ff:Tffd700{:05.2f}Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657intTb4b4b4(Te6edf3hoursTb4b4b4)Tb4b4b4,Tffa657intTb4b4b4(Te6edf3minutesTb4b4b4)Tb4b4b4,Te6edf3secondsTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff--- Statistics -----Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTime taken : Ta5d6ff"Tff7b72+Te6edf3timestringTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffData transferred : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3data_sentTb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTransfer rate : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3data_sentTff7b72/Te6edf3download_timeTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff/sTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)

Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)


T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3should_quit
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)

Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

Tff7b72def Td2a8ffclient_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72pass

T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################

T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSpeedtest exampleTa5d6ff"Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming requests from clientsTa5d6ff"
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)

Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)

Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None

Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)

Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────